package com.jgoodies.looks.plastic.ext;

import com.jgoodies.looks.LookUtils;
import com.jgoodies.looks.plastic.PlasticLookAndFeel;

import javax.swing.*;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.metal.MetalLookAndFeel;
import java.awt.*;
import java.io.Serializable;

/**
 * Created by IntelliJ IDEA.
 * User: babitsky.viacheslav
 * Date: 15.01.2008
 * Time: 15:24:02
 */
public class PlasticXPIconFactory {
    private static Icon frame_closeIcon;
    private static Icon frame_iconifyIcon;
    private static Icon frame_maxIcon;
    private static Icon frame_minIcon;
    private static final String FRAME_CLOSE_ICON = "frame_closeIcon";
    private static final String FRAME_ICONIFY_ICON = "frame_iconifyIcon";
    private static final String FRAME_MAX_ICON = "frame_maxIcon";
    private static final String FRAME_MIN_ICON = "frame_minIcon";

    private static CheckBoxIcon checkBoxIcon;
    private static RadioButtonIcon radioButtonIcon;

    public static Icon createFrameCloseIcon() {
        if (frame_closeIcon == null) {
            frame_closeIcon = new FrameButtonIcon(FRAME_CLOSE_ICON);
        }
        return frame_closeIcon;
    }

    public static Icon createFrameIconifyIcon() {
        if (frame_iconifyIcon == null) {
            frame_iconifyIcon = new FrameButtonIcon(FRAME_ICONIFY_ICON);
        }
        return frame_iconifyIcon;
    }

    public static Icon createFrameMaximizeIcon() {
        if (frame_maxIcon == null) {
            frame_maxIcon = new FrameButtonIcon(FRAME_MAX_ICON);
        }
        return frame_maxIcon;
    }

    public static Icon createFrameMinimizeIcon() {
        if (frame_minIcon == null) {
            frame_minIcon = new FrameButtonIcon(FRAME_MIN_ICON);
        }
        return frame_minIcon;
    }


    /**
     * Lazily creates and returns the check box icon.
     *
     * @return the check box icon
     */
    public static Icon getCheckBoxIcon() {
        if (checkBoxIcon == null) {
            checkBoxIcon = new CheckBoxIcon();
        }
        return checkBoxIcon;
    }

    /**
     * Lazily creates and returns the radio button icon.
     *
     * @return the check box icon
     */
    public static Icon getRadioButtonIcon() {
        if (radioButtonIcon == null) {
            radioButtonIcon = new RadioButtonIcon();
        }
        return radioButtonIcon;
    }


    private static class FrameButtonIcon implements Icon, Serializable {
        private String type;

        private FrameButtonIcon(String type) {
            this.type = type;
        }

        public void paintIcon(Component c, Graphics g, int x0, int y0) {
            JButton parentButton = (JButton) c;

            int width = getIconWidth();
            int height = getIconHeight();
            Color backgroundColor = PlasticLookAndFeel.getPrimaryControlShadow();

            if (parentButton.getClientProperty("paintActive") != Boolean.TRUE) {
                backgroundColor = MetalLookAndFeel.getControl();
            }
            // fill background
            g.setColor(backgroundColor);
            g.fillRect(0, 0, getIconWidth(), getIconHeight());

            if (parentButton.getModel().isPressed())
                g.setColor(Color.WHITE);
            else
                g.setColor(Color.BLACK);
//            int x = width / 12 + 2;
//            int y = height / 5;
//            int h = height - y * 2 - 1;
//            int w = width * 3 / 4 - 3;
            int empty = 3;
            int x = empty;
            int y = empty;
            int h = height - empty * 2;
            int w = width - empty * 2;
            int thickness2 = Math.max(height / 8, 2);
            int thickness = Math.max(width / 15, 1);
            if (type.equals(FRAME_CLOSE_ICON)) {
                int lineWidth;
                if (width > 47) lineWidth = 6;
                else if (width > 37) lineWidth = 5;
                else if (width > 26) lineWidth = 4;
                else if (width > 16) lineWidth = 3;
                else if (width > 12) lineWidth = 2;
                else lineWidth = 1;
                if (lineWidth == 1) {
                    if (w % 2 == 1) {
                        x++;
                        w++;
                    }
                    g.drawLine(x, y, x + w - 2, y + w - 2);
                    g.drawLine(x + w - 2, y, x, y + w - 2);
                } else {
                    x += 1;
                    w -= 1;
//                    x += 2;
//                    y++;
//                    w -= lineWidth;
                    g.drawLine(x, y, x + w - 1, y + w - 1);
                    g.drawLine(x + w - 1, y, x, y + w - 1);
                    g.drawLine(x + 1, y, x + w - 1, y + w - 2);
                    g.drawLine(x + w - 2, y, x, y + w - 2);
                    g.drawLine(x, y + 1, x + w - 2, y + w - 1);
                    g.drawLine(x + w - 1, y + 1, x + 1, y + w - 1);
                    for (int i = 4; i <= lineWidth; i++) {
                        g.drawLine(x + i - 2, y, x + w - 1, y + w - i + 1);
                        g.drawLine(x, y + i - 2, x + w - i + 1, y + w - 1);
                        g.drawLine(x + w - i + 1, y, x, y + w - i + 1);
                        g.drawLine(x + w - 1, y + i - 2, x + i - 2, y + w - 1);
                    }
                }
            } else if (type.equals(FRAME_ICONIFY_ICON)) {
                g.fillRect(x, y + h - thickness2, w - w / 3, thickness2);
            } else if (type.equals(FRAME_MAX_ICON)) {
                g.fillRect(x, y, w, thickness2);
                g.fillRect(x, y, thickness, h);
                g.fillRect(x + w - thickness, y, thickness, h);
                g.fillRect(x, y + h - thickness, w, thickness);
            } else if (type.equals(FRAME_MIN_ICON)) {
                g.fillRect(x + w / 3, y, w - w / 3, thickness2);
                g.fillRect(x + w / 3, y, thickness, h / 3);
                g.fillRect(x + w - thickness, y, thickness, h - h / 3);
                g.fillRect(x + w - w / 3, y + h - h / 3 - thickness, w / 3, thickness);

                g.fillRect(x, y + h / 3, w - w / 3, thickness2);
                g.fillRect(x, y + h / 3, thickness, h - h / 3);
                g.fillRect(x + w - w / 3 - thickness, y + h / 3, thickness, h - h / 3);
                g.fillRect(x, y + h - thickness, w - w / 3, thickness);
            }
        }

        public int getIconWidth() {
            int width = UIManager.getInt("InternalFrame.titleButtonWidth");
            if (width == 0) return 16;
            return width;
        }

        public int getIconHeight() {
            int height = UIManager.getInt("InternalFrame.titleButtonHeight");
            if (height == 0) return 16;
            return height;
        }
    }

    /**
     * Paints the the icon and focus border for Plastic XP check boxes.
     */
    private static final class CheckBoxIcon implements Icon, UIResource, Serializable {

        private static final int SIZE = LookUtils.IS_LOW_RESOLUTION ? 13 : 15;

        public int getIconWidth() {
            return SIZE;
        }

        public int getIconHeight() {
            return SIZE;
        }

        public void paintIcon(Component c, Graphics g, int x, int y) {
            JCheckBox cb = (JCheckBox) c;
            ButtonModel model = cb.getModel();
            Graphics2D g2 = (Graphics2D) g;
            boolean paintFocus = (model.isArmed() && !model.isPressed())
                    || (cb.hasFocus() && isBlank(cb.getText()));

            final RenderingHints.Key key = RenderingHints.KEY_ANTIALIASING;
            Object newAAHint = RenderingHints.VALUE_ANTIALIAS_ON;
            Object oldAAHint = g2.getRenderingHint(key);
            if (newAAHint != oldAAHint) {
                g2.setRenderingHint(key, newAAHint);
            } else {
                oldAAHint = null;
            }

            drawBorder(g2, model.isEnabled(), x, y, SIZE - 1, SIZE - 1);
            drawFill(g2, model.isPressed(), x + 1, y + 1, SIZE - 2, SIZE - 2);
            if (paintFocus) {
                drawFocus(g2, x + 1, y + 1, SIZE - 3, SIZE - 3);
            }
            if (model.isSelected()) {
                drawCheck(g2, model.isEnabled(), x + 3, y + 3, SIZE - 7, SIZE - 7);
            }

            if (oldAAHint != null) {
                g2.setRenderingHint(key, oldAAHint);
            }
        }

        private void drawBorder(Graphics2D g2, boolean enabled, int x, int y, int width, int height) {
            g2.setColor(enabled
                    ? PlasticLookAndFeel.getControlDarkShadow()
                    : MetalLookAndFeel.getControlDisabled());
            g2.drawRect(x, y, width, height);
        }

        private void drawCheck(Graphics2D g2, boolean enabled, int x, int y, int width, int height) {
            g2.setColor(enabled
                    ? UIManager.getColor("CheckBox.check")
                    : MetalLookAndFeel.getControlDisabled());
            int right = x + width;
            int bottom = y + height;
            int startY = y + height / 3;
            int turnX = x + width / 2 - 2;
            g2.drawLine(x, startY, turnX, bottom - 3);
            g2.drawLine(x, startY + 1, turnX, bottom - 2);
            g2.drawLine(x, startY + 2, turnX, bottom - 1);
            g2.drawLine(turnX + 1, bottom - 2, right, y);
            g2.drawLine(turnX + 1, bottom - 1, right, y + 1);
            g2.drawLine(turnX + 1, bottom, right, y + 2);
        }

        private void drawFill(Graphics2D g2, boolean pressed, int x, int y, int w, int h) {
            Color upperLeft;
            Color lowerRight;
            if (pressed) {
                upperLeft = MetalLookAndFeel.getControlShadow();
                lowerRight = PlasticLookAndFeel.getControlHighlight();
            } else {
                upperLeft = PlasticLookAndFeel.getControl();
                lowerRight = PlasticLookAndFeel.getControlHighlight().brighter();
            }
            g2.setPaint(new GradientPaint(x, y, upperLeft, x + w, y + h, lowerRight));
            g2.fillRect(x, y, w, h);
        }

        private void drawFocus(Graphics2D g2, int x, int y, int width, int height) {
            g2.setPaint(new GradientPaint(
                    x,
                    y,
                    PlasticLookAndFeel.getFocusColor().brighter(),
                    width,
                    height,
                    PlasticLookAndFeel.getFocusColor() /*.darker()*/
            ));
            g2.drawRect(x, y, width, height);
            g2.drawRect(x + 1, y + 1, width - 2, height - 2);
        }

    }


    /**
     * Paints the the icon and focus border for Plastic XP check boxes.
     */
    private static final class RadioButtonIcon implements Icon, UIResource, Serializable {

        private static final int SIZE = LookUtils.IS_LOW_RESOLUTION ? 13 : 15;

        private static final Stroke FOCUS_STROKE = new BasicStroke(2);

        public int getIconWidth() {
            return SIZE;
        }

        public int getIconHeight() {
            return SIZE;
        }

        public void paintIcon(Component c, Graphics g, int x, int y) {
            Graphics2D g2 = (Graphics2D) g;
            AbstractButton rb = (AbstractButton) c;
            ButtonModel model = rb.getModel();
            boolean paintFocus = (model.isArmed() && !model.isPressed())
                    || (rb.hasFocus() && isBlank(rb.getText()));

            final RenderingHints.Key key = RenderingHints.KEY_ANTIALIASING;
            Object newAAHint = RenderingHints.VALUE_ANTIALIAS_ON;
            Object oldAAHint = g2.getRenderingHint(key);
            if (newAAHint != oldAAHint) {
                g2.setRenderingHint(key, newAAHint);
            } else {
                oldAAHint = null;
            }

            drawFill(g2, model.isPressed(), x, y, SIZE - 1, SIZE - 1);
            if (paintFocus) {
                drawFocus(g2, x + 1, y + 1, SIZE - 3, SIZE - 3);
            }
            if (model.isSelected()) {
                drawCheck(g2, c, model.isEnabled(), x + 4, y + 4, SIZE - 8, SIZE - 8);
            }
            drawBorder(g2, model.isEnabled(), x, y, SIZE - 1, SIZE - 1);

            if (oldAAHint != null) {
                g2.setRenderingHint(key, oldAAHint);
            }
        }

        private void drawBorder(Graphics2D g2, boolean enabled, int x, int y, int w, int h) {
            g2.setColor(enabled
                    ? PlasticLookAndFeel.getControlDarkShadow()
                    : MetalLookAndFeel.getControlDisabled());
            g2.drawOval(x, y, w, h);
        }

        private void drawCheck(Graphics2D g2, Component c, boolean enabled, int x, int y, int w, int h) {
            g2.translate(x, y);
            if (enabled) {
                g2.setColor(UIManager.getColor("RadioButton.check"));
                g2.fillOval(0, 0, w, h);
                UIManager.getIcon("RadioButton.checkIcon").paintIcon(c, g2, 0, 0);
            } else {
                g2.setColor(MetalLookAndFeel.getControlDisabled());
                g2.fillOval(0, 0, w, h);
            }
            g2.translate(-x, -y);
        }

        private void drawFill(Graphics2D g2, boolean pressed, int x, int y, int w, int h) {
            Color upperLeft;
            Color lowerRight;
            if (pressed) {
                upperLeft = MetalLookAndFeel.getControlShadow();
                lowerRight = PlasticLookAndFeel.getControlHighlight();
            } else {
                upperLeft = PlasticLookAndFeel.getControl();
                lowerRight = PlasticLookAndFeel.getControlHighlight().brighter();
            }
            g2.setPaint(new GradientPaint(x, y, upperLeft, x + w, y + h, lowerRight));
            g2.fillOval(x, y, w, h);
        }

        private void drawFocus(Graphics2D g2, int x, int y, int w, int h) {
            g2.setPaint(
                    new GradientPaint(
                            x,
                            y,
                            PlasticLookAndFeel.getFocusColor().brighter(),
                            w,
                            h,
                            PlasticLookAndFeel.getFocusColor()));
            Stroke stroke = g2.getStroke();
            g2.setStroke(FOCUS_STROKE);
            g2.drawOval(x, y, w, h);
            g2.setStroke(stroke);
        }

    }

    // Helper Code ************************************************************

    /**
     * Checks and answers if the given string is whitespace,
     * empty ("") or <code>null</code>.
     * <p/>
     * <pre>
     * isBlank(null)    == true
     * isBlank("")      == true
     * isBlank(" ")     == true
     * isBlank(" abc")  == false
     * isBlank("abc ")  == false
     * isBlank(" abc ") == false
     * </pre>
     *
     * @param str the string to check, may be <code>null</code>
     * @return <code>true</code> if the string is whitespace, empty
     *         or <code>null</code>
     */
    private static boolean isBlank(String str) {
        int length;
        if ((str == null) || ((length = str.length()) == 0))
            return true;
        for (int i = length - 1; i >= 0; i--) {
            if (!Character.isWhitespace(str.charAt(i)))
                return false;
        }
        return true;
    }


}


